import click
import time
from .api_wrapper import request_data
from .scanners import nessus_scanners
from .database import new_db_connection
from .error_msg import error_msg
from .licensed_count import get_licensed
from sqlite3 import Error
import textwrap


@click.command(help="Display or Print information found in Tenable.io")
@click.option('-scanners', is_flag=True, help="List all of the Scanners")
@click.option('-users', is_flag=True, help="List all of the Users")
@click.option('-exclusions', is_flag=True, help="List all Exclusions")
@click.option('-containers', is_flag=True, help="List all containers and their Vulnerability  Scores")
@click.option('-logs', is_flag=True, help="List The actor and the action in the log file")
@click.option('-running', is_flag=True, help="List the running Scans")
@click.option('-scans', is_flag=True, help="List all Scans")
@click.option('-nnm', is_flag=True, help="Nessus Network Monitor assets and their vulnerability scores")
@click.option('-assets', is_flag=True, help="Assets found in the last 30 days")
@click.option('-policies', is_flag=True, help="Scan Policies")
@click.option('-connectors', is_flag=True, help="List Connector Details and Status")
@click.option('-agroup', is_flag=True, help="List Access Groups and Status")
@click.option('-status', is_flag=True, help="Print T.io Status and Account info")
@click.option('-agents', is_flag=True, help="Print Agent information")
@click.option('-webapp', is_flag=True, help='Print Web App Scans')
@click.option('-tgroup', is_flag=True, help='Print Target Groups')
@click.option('-licensed', is_flag=True, help='Print License information')
@click.option('-tags', is_flag=True, help='Print Tag Categories and values')
@click.option('-categories', is_flag=True, help='Print all of the Tag Categories and their UUIDs')
@click.option('-smtp', is_flag=True, help="Print your smtp information")
@click.option('-cloud', is_flag=True, help="Print Cloud assets found in the last 30 days by the connectors")
@click.option('-networks', is_flag=True, help="Print Network IDs")
@click.option('-version', is_flag=True, help="Display current version of Navi")
def display(scanners, users, exclusions, containers, logs, running, scans, nnm, assets, policies, connectors, agroup, status, agents, webapp, tgroup, licensed, tags, categories, smtp, cloud, networks, version):

    if scanners:
        nessus_scanners()

    if users:
        try:
            data = request_data('GET', '/users')
            print("\nUser Name".ljust(35), "Login email".ljust(40), "User UUID")
            print("-" * 100)
            for user in data["users"]:
                print(str(user["name"]).ljust(34), str(user["username"]).ljust(40), user['uuid'])
            print()

        except Exception as E:
            error_msg(E)

    if exclusions:
        try:
            data = request_data('GET', '/exclusions')
            for x in data["exclusions"]:
                print("\nExclusion Name : ", x["name"], '\n')
                print(x["members"], '\n')

        except KeyError:
            print("No Exclusions Set, or there could be an issue with your API keys")

    if containers:
        try:
            data = request_data('GET', '/container-security/api/v2/images?limit=1000')
            print("\nContainer Name".ljust(35), "Repository ID".ljust(35), "Tag".ljust(15), "Docker ID".ljust(15), "# of Vulns".ljust(10))
            print("-" * 125)
            try:
                for images in data["items"]:
                    print(str(images["name"]).ljust(35), str(images["repoName"]).ljust(35), str(images["tag"]).ljust(15), str(images["imageHash"]).ljust(15), str(images["numberOfVulns"]).ljust(25))
            except KeyError:
                pass
            print()
        except Exception as E:
            error_msg(E)

    if logs:
        try:
            data = request_data('GET', '/audit-log/v1/events')
            print("\nEvent Date".ljust(24), "Action Taken".ljust(30), "User".ljust(30))
            print("-" * 100)
            for log in data['events']:
                received = log['received']
                action = log['action']
                actor = log['actor']['name']
                print(str(received).ljust(24), str(action).ljust(30), str(actor))

        except Exception as E:
            error_msg(E)
        print()

    if running:
        try:
            data = request_data('GET', '/scans')
            run = 0
            print("\nScan Name".ljust(61), "Scan ID".ljust(10), "Status".ljust(30))
            print("-" * 100)
            for scan in data['scans']:
                if scan['status'] == "running":
                    run = run + 1
                    name = scan['name']
                    scan_id = scan['id']
                    current_status = scan['status']

                    print(str(name).ljust(60), str(scan_id).ljust(10), str(current_status))
            print()

            if run == 0:
                print("No running scans")
        except Exception as E:
            error_msg(E)

    if scans:
        print()
        print("*" * 40)
        print("This endpoint is extremely limited. 1 to 2 calls per min")
        print("If you get a 429 error, wait a minute and try again")
        print("*" * 40)
        try:
            data = request_data('GET', '/scans')
            print("\nScan Name".ljust(61), "Scan ID".ljust(10), "Status".ljust(30))
            print("-" * 100)
            for scan in data['scans']:
                name = scan['name']
                scan_id = scan['id']
                scan_status = scan['status']
                print(str(name).ljust(60), str(scan_id).ljust(10), str(scan_status))
            print()

        except Exception as E:
            error_msg(E)

    if nnm:
        try:
            # dynamically find the PVS sensor
            nnm_data = request_data('GET', '/scans')

            for scan in nnm_data["scans"]:

                if str(scan["type"]) == "pvs":
                    nnm_id = scan["id"]
                    try:
                        data = request_data('GET', '/scans/' + str(nnm_id) + '/')
                        if len(data["hosts"]) != 0:
                            print("Here are the assets and their scores last found by Nessus Network Monitor")
                            print("   IP Address     : Score")
                            print("----------------")

                            for host in data["hosts"]:
                                print(str(host["hostname"]) + " :  " + str(host["score"]))
                            print()
                    except KeyError:
                        pass
        except Exception as E:
            error_msg(E)

    if assets:
        try:
            data = request_data('GET', '/workbenches/assets/?date_range=30') #, params=asset_limit)

            print("\nBelow are the assets found in the last 30 days")
            print("\nIP Address(es)".ljust(35), "FQDN(s)".ljust(65), "Exposure Score".ljust(15), "Sources")
            print("-" * 120)
            for asset in data["assets"]:
                sources = []
                for source in asset["sources"]:
                    sources.append(source['name'])

                print(str(asset["ipv4"]).ljust(36), str(asset["fqdn"]).ljust(64), str(asset["exposure_score"]).ljust(15), sources)

        except Exception as E:
            error_msg(E)

    if policies:
        try:
            data = request_data('GET', '/policies')
            print("\nPolicy Name".ljust(41), "Description".ljust(61), "Template ID")
            print("-" * 100)
            for policy in data['policies']:
                print(str(policy['name']).ljust(40), str(policy['description']).ljust(60), policy['template_uuid'] )
            print()
        except Exception as E:
            error_msg(E)

    if connectors:
        try:
            data = request_data('GET', '/settings/connectors')
            print("\nType".ljust(11), "Connector Name".ljust(40), "Connnector ID".ljust(40), "Last Sync".ljust(30), "Schedule ")
            print("-" * 150)
            for conn in data["connectors"]:
                schedule = str(conn['schedule']['value']) + " " + str(conn['schedule']['units'])
                try:
                    last_sync = conn['last_sync_time']

                except KeyError:
                    last_sync = "Hasn't synced"
                print(str(conn['type']).ljust(10), str(conn['name']).ljust(40), str(conn['id']).ljust(40), last_sync.ljust(30), schedule)
        except Exception as E:
            error_msg(E)
        print()

    if agroup:
        try:
            data = request_data('GET', '/access-groups')
            print("\nGroup Name".ljust(26), "Group ID".ljust(40), "Last Updated".ljust(25), "Rules")
            print("-" * 120)
            for group in data["access_groups"]:
                try:
                    updated = group['updated_at']
                except KeyError:
                    updated = "Not Updated"
                details = request_data('GET', '/access-groups/'+str(group['id']))

                try:
                    for rule in details['rules']:
                        rules = str(rule['terms'])
                except KeyError:
                    rules = "Not Rule based"
                print(str(group['name']).ljust(25), str(group['id']).ljust(40), str(updated).ljust(25), textwrap.shorten(rules, width=60))
            print()
        except Exception as E:
            error_msg(E)

    if status:
        try:
            data = request_data('GET', "/server/properties")
            session_data = request_data('GET', "/session")

            print("\nTenable IO Information")
            print("-----------------------")
            print("Container ID : ", session_data["container_id"])
            print("Container UUID :", session_data["container_uuid"])
            print("Container Name : ", session_data["container_name"])
            print("Site ID :", data["analytics"]["site_id"])
            print("Region : ", data["region"])

            print("\nLicense information")
            print("--------------------")
            print("Licensed Assets : ", get_licensed())
            print("Agents Used : ", data["license"]["agents"])
            print("Expiration Date : ", data["license"]["expiration_date"])
            print("Scanners Used : ", data["license"]["scanners"])
            print("Users : ", data["license"]["users"])
            print("\nEnabled Apps")
            print("---------")
            print()
            for key in data["license"]["apps"]:
                print(key)
                print("-----")
                try:
                    print("Expiration: ", data["license"]["apps"][key]["expiration_date"])

                except KeyError:
                    pass
                print("Mode: ", data["license"]["apps"][key]["mode"])
                print()

        except Exception as E:
            error_msg(E)

    if agents:
        querystring = {"limit": "5000"}
        try:
            data = request_data('GET', '/scanners/1/agents', params=querystring)
            print("\nAgent Name".ljust(46), "IP Address".ljust(20), "Last Connect Time".ljust(20), "Last Scanned Time".ljust(20), "Status".ljust(10), "Groups")
            print("-" * 140)
            for agent in data['agents']:
                last_connect = agent['last_connect']
                last_connect_time = time.strftime("%b %d %H:%M:%S", time.localtime(last_connect))

                try:
                    last_scanned = agent['last_scanned']
                    last_scanned_time = time.strftime("%b %d %H:%M:%S", time.localtime(last_scanned))
                except KeyError:
                    # I assume if we can't pull as scanned time, it doesn't exist
                    last_scanned_time = "Not Scanned"
                groups = ''
                try:
                    for group in agent['groups']:
                        groups = groups + ", " + group['name']
                except KeyError:
                    pass
                print(str(agent['name']).ljust(45), str(agent['ip']).ljust(20), str(last_connect_time).ljust(20),
                      str(last_scanned_time).ljust(20), str(agent['status']).ljust(10), str(groups[1:]))
        except Exception as E:
            error_msg(E)
        print()

    if webapp:
        print()
        print("*" * 40)
        print("This is using the V1 and Was 1.0 endpoints")
        print("use the 'navi was --help' to see what is available for v2")
        print("*" * 40)
        try:
            data = request_data('GET', '/scans')
            print("\nScan Name".ljust(61), "Scan ID".ljust(10), "Status".ljust(30))
            print("-" * 100)
            for scan in data['scans']:
                if scan['type'] == 'webapp':
                    name = scan['name']
                    scan_id = scan['id']
                    scan_status = scan['status']
                    print(str(name).ljust(60), str(scan_id).ljust(10), str(scan_status))
            print()

        except Exception as E:
            error_msg(E)

    if tgroup:
        print()
        print("*" * 40)
        print("Target Groups are going to be retired use the Migration script to covert them to tags")
        print("https://github.com/packetchaos/tio_automation/blob/master/migrate_target_groups.py")
        print("*" * 40)
        data = request_data('GET', '/target-groups')
        print("\nTarge Group Name".ljust(41), "TG ID".ljust(10), "Owner".ljust(30), "Members")
        print("-" * 100)
        try:
            for targets in data['target_groups']:
                mem = targets['members']
                print(str(targets['name']).ljust(40), str(targets['id']).ljust(10), str(targets['owner']).ljust(30), textwrap.shorten(mem, width=60))
            print()
        except Exception as E:
            error_msg(E)

    if licensed:
        print("\nLicensed Count : ", get_licensed())
        print()
        database = r"navi.db"
        conn = new_db_connection(database)
        with conn:
            cur = conn.cursor()
            cur.execute("SELECT ip_address, fqdn, last_licensed_scan_date from assets where last_licensed_scan_date !=' ';")
            data = cur.fetchall()

            print("IP Address".ljust(20), "Full Qualified Domain Name".ljust(65), "Licensed Date")
            print("-" * 120)
            print()
            for asset in data:
                ipv4 = asset[0]
                fqdn = asset[1]
                licensed_date = asset[2]
                # Don't display Web applications in this output
                if ipv4 != " ":
                    print(str(ipv4).ljust(20), str(fqdn).ljust(65), licensed_date)
        print()

    if tags:
        data = request_data('GET', '/tags/values')
        print("\nTags".ljust(33), "Value".ljust(35), "Value UUID")
        print('-'.rjust(92, '-'), "\n")
        for tag_values in data['values']:
            try:
                tag_value = tag_values['value']
                uuid = tag_values['uuid']
            except KeyError:
                tag_value = "Value Not Set Yet"
                uuid = "NO Value set"
            print(str(tag_values['category_name']).rjust(30), ":", str(tag_value).ljust(35), str(uuid).ljust(25))
        print()

    if categories:
        data = request_data('GET', '/tags/categories')
        print("\nTag Categories".ljust(31), "Category UUID")
        print('-'.rjust(50, '-'), "\n")
        for cats in data['categories']:

            category_name = cats['name']
            category_uuid = cats['uuid']

            print(str(category_name).ljust(30), str(category_uuid).ljust(25))
        print()

    if smtp:
        try:
            database = r"navi.db"
            conn = new_db_connection(database)
            with conn:
                cur = conn.cursor()
                cur.execute("SELECT server, port, from_email from smtp;")
                data = cur.fetchall()
                for settings in data:
                    print("\nYour email server: {}".format(settings[0]))
                    print("The email port is: {}".format(settings[1]))
                    print("Your email is: {}\n".format(settings[2]))
        except Error as E:
            print("\nYou have no SMTP information saved.\n")
            print("Error: ", E, "\n")

    if cloud:
        query = {"date_range": "30", "filter.0.filter": "sources", "filter.0.quality": "set-has", "filter.0.value": "AWS",
                 "filter.1.filter": "sources", "filter.1.quality": "set-has", "filter.1.value": "AZURE",
                 "filter.2.filter": "sources", "filter.2.quality": "set-has", "filter.2.value": "GCP", "filter.search_type":"or"}
        data = request_data('GET', '/workbenches/assets', params=query)
        print("\nSource".ljust(11), "IP".ljust(15), "FQDN".ljust(45), "UUID".ljust(40), "First seen")
        print("-" * 150)
        for assets in data['assets']:
            for source in assets['sources']:
                if source['name'] != 'NESSUS_SCAN':
                    asset_ip = assets['ipv4'][0]
                    uuid = assets['id']
                    try:
                        asset_fqdn = assets['fqdn'][0]
                    except IndexError:
                        asset_fqdn = "NO FQDN found"

                    print(source['name'].ljust(10), asset_ip.ljust(15), asset_fqdn.ljust(45),uuid.ljust(40), source['first_seen'])
        print()

    if networks:
        try:
            data = request_data('GET', '/networks')
            print("\nNetwork Name".ljust(26), "# of Scanners".ljust(16), "UUID")
            print("-" * 100)
            for network in data['networks']:
                print(network['name'].ljust(25), str(network['scanner_count']).ljust(15), network['uuid'])
        except Exception as E:
            error_msg(E)
        print()

    if version:
        print("\nNavi Version 5.2.0\n")
